class Point1 {
    x: number;
    y: number;
}

const pt = new Point1();
pt.x = 0;
pt.y = 0;


class Point2 {
    x = 0;
    y = 0;
}

const pt2 = new Point2();
// Prints 0, 0
console.log(`${pt2.x} ${pt2.y}`);
// Type 'string' is not assignable to type 'number'
pt2.x = "0";

class BadGreeter {
    // Property 'name' has no initializer and is not definitely assigned in the constructor.
    name: string;
}

class GoodGreeter {
    name: string;
    constructor() {
        this.name = "hello";
    }
}

class OKGreeter {
    // Not initialized, but no error
    name!: string;
}

class Greeter {
    readonly name: string = "world";

    constructor(otherName?:string) {
        if(otherName !== undefined) {
            this.name = otherName;
        }
    } 
    error() {
        // Cannot assign to 'name' because it is a read-only property.
        this.name = "not ok";
    }
}

const g = new Greeter();
 // Cannot assign to 'name' because it is a read-only property.
g.name = "also not ok";

// Constructors
class Point3 {
    x: number;
    y: number;
    // Normal signature with defaults
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }
}

class Point4 {
    x: number;
    y: number;
    // Overloads
    constructor(x:number, y: string);
    constructor(s:string);
    constructor(xs:any, y?:any) {
        
    }
}

// super calls
class Base {
    k = 4;
}

class Derived extends Base {
    constructor() {
        // 'super' must be called before accessing 'this' in the constructor of a derived class.
        console.log(this.k);
        super();
    }
}
// Methods
class Point5 {
    x = 10;
    y = 10;
    scale(n: number):void {
        this.x *= n;
        this.y *= n;
    }
}
// 成员类的数据要加this
let x1:number = 0;
class C {
    x1: string = "hello";

    m() {
        // Type 'string' is not assignable to type 'number'.
        x1 = "world";
    }
}

// Getters/Setters
class C1 {
    _length = 0;
    get length() {
        return this._length;
    }

    set length(value) {
        this._length = value;
    }
}

// 只有get没有set, 属性默认是只读
// 没有指定类型，会自动推导
// get set，有相同的成员可见性
class Thing {
    _size = 0;

    get size() {
        return this._size;
    }

    set size(value: string | number | boolean) {
        let num = Number(value);
        // Don't allow NaN, Infinity, etc
        if(!Number.isFinite(num)) {
            this._size = 0;
            return;
        }
        this._size = num;
    } 
}


class MyClass {
    [s:string]:boolean | ((s:string) => boolean);

    check(s:string) {
        return this[s] as boolean;
    }
}
// class Heriage
interface Pingable {
    ping(): void;
}

class Sonar implements Pingable {
    ping(): void {
        console.log("Ping!");
    }
}
// Class 'Ball' incorrectly implements interface 'Pingable'.
//   Property 'ping' is missing in type 'Ball' but required in type 'Pingable'
class Ball implements Pingable {
    // 
    p0ng(): void {
        
    }
}


interface Checkable {
    check(name: string): boolean;
}

class NameChecker implements Checkable {
    // Parameter 's' implicitly has an 'any' type, but a better type may be inferred from usage
    check(s): boolean {
        // (parameter) s: any
        return s.toLocaleLowerCase() === " ok";
    }
}

interface A1 {
    x: number;
    y?: number;
}

class C2 implements A1 {
    x = 0;
}

const c2 = new C2();
// Property 'y' does not exist on type 'C2'
c2.y = 10;

class Animal1 {
    move() {
        console.log("Moving along!");
    }
}

class Dog extends Animal1 {
    woof(times: number) {
        for (let i = 0; i < times; i++) {
            console.log("woof");
        }
    }
}

const d = new Dog();
// Base class method
d.move();
// Derived class method
d.woof(4);


class Base1 {
    greet() {
        console.log("Hello world!");
    }
}

class Derived1 extends Base1 {
    /**
     * 没有问号的时候就会报错
     * roperty 'greet' in type 'Derived1' is not assignable to the same property in base type 'Base1'.
     * Type '(name: string) => void' is not assignable to type '() => void'.
     * Target signature provides too few arguments. Expected 1 or more, but got 0
     */
    greet(name?:string): void {
        if(name === undefined) {
            super.greet();
        } else {
            console.log(`Hello, ${name.toLowerCase()}`);
        }
    }
}

const d1 = new Derived1();
d1.greet();
d1.greet("reader");
// Alias the derived instance through a base class reference
const b1: Base1 = d1;
// no problem
b1.greet();
// Type-only Field Declarations
interface Animal2 {
    dateOfBirth: any;
}

interface Dog extends Animal2 {
    breed: any;
}

class AnimalHouse {
    resident: Animal2;
    constructor(animal: Animal2) {
        this.resident = animal;
    }
}

class DogHouse extends AnimalHouse {
    // Does not emit JavaScript code
    // only ensures the types are correct
    declare resident: Dog;
    constructor(dog: Dog) {
        super(dog);
    }
}

// Initialization Order
class Base3 {
    name = "base3";
    constructor() {
        console.log("My names is " + this.name);
    }
}

class Derived2 extends Base {
    name = "derived";
}
const d2 = new Derived2();
// The base class fields are initialized
// The base class constructor runs
// The derived class fields are initialized
// The derived class constructor runs
class MsgError extends Error {
    constructor(m: string) {
        super(m);
        Object.setPrototypeOf(this, MsgError.prototype);
    }

    sayHello() {
        return "Hello " + this.message;
    }
}

// Member visibility
class Greeter2 {
    public greet() {
        console.log("hi!");
    }
}

const g1 = new Greeter2();
g1.greet();

class Greeter3 {
    public greet() {
        console.log("Hello, " + this.getName());
    }

    protected getName() {
        return "hi";
    }
}


class SpecialGreeter extends Greeter3 {
    public howdy() {
        console.log("Howdy " + this.getName());
    }
}

const g3 = new SpecialGreeter();
g3.greet();
// Property 'getName' is protected and only accessible within class 'Greeter3' and its 
g3.getName();

class Base4 {
    protected m = 4;
}

class Derived4 extends Base {
    // No modifier, so default is 'public'
    m = 15;
}

const d4 = new Derived4();
console.log(d4.m); // it's ok 重新声明了一个变量

class Base5 {
    protected x: number = 1;
}

class Derived5 extends Base5 {
    protected x: number = 5;
}
// 不能跨层级访问protected
class Derived6 extends Base5 {
    f1(other: Derived6) {
        other.x = 10;
      }
    f2(other: Derived5) {
        // Property 'x' is protected and only accessible within class 'Derived5' and its subclasses
        other.x = 10;
    }
}

// private
class Base6 {
    private x = 0;
}

const b = new Base6();
// Property 'x' is private and only accessible within class 'Base6'
console.log(b.x);
// Class 'Derived7' incorrectly extends base class 'Base6'.
//   Property 'x' is private in type 'Base6' but not in type 'Derived7'
class Derived7 extends Base6 {
    x = 1;
}

class A2 {
    private x = 10;
    public sameAs(other:A2) {
        return other.x === this.x;
    }
}
// private 和 protected只是 type检查，实际上在JavaScript中仍能访问

class MySafe {
    private secretKey = 1234;
}

const s = new MySafe();
// Property 'secretKey' is private and only accessible within class 'MySafe'.
console.log(s.secretKey);
// OK
console.log(s["secretKey"]);
"use strict"
class Dog1 {
    #barkAmount = 0;
    personality = "happy";
    constructor(){}
}

// static Members
class MyClass1 {
    static x = 0;
    static printX() {
        console.log(MyClass1.x);
    }
}

console.log(MyClass1.x);
MyClass1.printX();

// 也能添加private protected, public可见性修饰符
class MyClass2 {
    private static x = 0;
}
// Property 'x' is private and only accessible within class 'MyClass2'
console.log(MyClass2.x)

class Base8 {
    static getGreeting() {
        return "Hello world";
    }
}

class Derived8 extends Base8 {
    myGreeting = Derived8.getGreeting();
}

// Special static names
class S {
    // name, length, call 都不能用于static; new 
    // Static property 'name' conflicts with built-in property 'Function.name' of constructor function 'S'
    static name = "S!"
}

// Why no static classes? 为啥没有静态类
// 'static' modifier cannot appear on a module or namespace element.
static class Test{

}
// Unnecessary "static" class
class MyStaticClass {
    static doSomething() {}
}
// Perferred (alternative 1)
function doSomething() {}
// Perferred (alternative 2)
const MyHelperObject = {
    doSomething() {}
};


// static blocks in classes
class Foo {
    static #count = 0;
    getCount() {
        return Foo.#count;
    }

    static {
        try {
            const lastInstances =  [1,23];
            Foo.#count += lastInstances.length;
        }
        catch {

        }
    }
}
// Generic Classes
class Box<Type> {
    contents: Type;
    constructor(value:Type) {
        this.contents = value;
    }
}
// const b2: Box<string>
const b2 = new Box("hello");
// 不用于静态参数上
class Box<Type> {
    // Static members cannot reference class type parameters.
    static defaultValue:Type;
}
// 类型会被擦除

// this at runtime in classes
// typescript不会改变javascript的行为，

class MyClass3 {
    name = "MyClass3";
    getName() {
        return this.name;
    }
}

const c = new MyClass3();
const obj = {
    name: "obj",
    // c中的this就是obj的，并不能扩类
    getName: c.getName,
}
// print "obj", not "myClass"
console.log(obj.getName())

// Arrow Functions 箭头函数
class MyClass4 {
    name = "MyClass4";
    getName = () => {
        return this.name;
    };
}

const c1 = new MyClass4();
const g2 = c1.getName;

console.log(g2());

class MyClass5 {
    name = "MyClass";
    getName(this: MyClass5) {
        return this.name;
    }
}

const c3 = new MyClass5();
c3.getName();

// Error, would crash
const g4 = c3.getName;
// The 'this' context of type 'void' is not assignable to method's 'this' of type 'MyClass5'
console.log(g4())

// this types
class Box2 {
    contents:string = "";
    set(value:string) {
        this.contents = value;
        return this;
    }

    sameAs(other:this) {
        return other.contents === this.contents;
    }
}


class ClearableBox extends Box2 {
    clear() {
        this.contents = "";
    }
}

const a = new ClearableBox();
// const b3: ClearableBox
const b3 = a.set("hello");

class DerivedBox extends Box2 {
    otherContent:string = "?";
}

const base9 = new Box2();
const derived9 = new DerivedBox();
// Argument of type 'Box2' is not assignable to parameter of type 'DerivedBox'.
//   Property 'otherContent' is missing in type 'Box2' but required in type 'DerivedBox'
// this就是本类，不是父类
derived9.sameAs(base9);
// this -based type guards

class FileSystemObject {
    isFile(): this is FileRep {
        return this instanceof FileRep;
    }

    isDirectory(): this is Directory {
        return this instanceof Directory;
    }

    isNetworked(): this is Networked & this {
        return this.networked
    }

    constructor(public path:string, private networked: boolean) {}
}

class FileRep extends FileSystemObject {
    constructor(path:string, public content:string) {
        super(path, false);
    }
}


class Directory extends FileSystemObject {
    children:FileSystemObject[];
}


interface Networked {
    host:string;
}

const fso: FileSystemObject = new FileRep("foo/bar.txt", "foo");

if(fso.isFile()) {
    // const fso: FileRep
    fso.content;
} else if(fso.isDirectory()) {
    // const fso: Directory
    fso.children;
} else if(fso.isNetworked()) {
    // const fso: Networked & FileSystemObject
    fso.host;
}

class Box3<T> {
    value?: T;

    hasValue():this is {value:T} {
        return this.value !== undefined;
    }
}


const box = new Box3();
box.value = "Gameboy";
// (property) Box3<unknown>.value?: unknown
box.value

if(box.hasValue()) {
    // (property) value: unknown
    box.value;
}

// Parameter Properties
class Params {
    constructor(
        public readonly x: number,
        protected y: number,
        private z: number
    ) {
        // No body necessary
    }
}

const a1 = new Params(1, 2, 3);
console.log(a1.x);
// Property 'z' is private and only accessible within class 'Params'
console.log(a1.z);

// class Expressions

const someClass = class<Type> {
    content:Type;
    constructor(value:Type) {
        this.content = value;
    }
}
// const m: someClass<string>
const m = new someClass("Hello, world");
// Constructor Signatures
class Point {
    createdAt: number;
    x: number;
    y: number;

    constructor(x: number, y: number) {
        this.createdAt = Date.now();
        this.x = x;
        this.y = y;
    }
}

type PointInstance = InstanceType<typeof Point>
function moveRight(point: PointInstance) {
    point.x += 5;
}

const point = new Point(3, 4);
moveRight(point);
point.x // => 8


// abstract classes and members
abstract class Base10 {
    abstract getName():string;

    printName() {
        console.log("Hello, " + this.getName());
    }
}
// Cannot create an instance of an abstract class.
const b10 = new Base10();
// Non-abstract class 'derived10' does not implement all abstract members of 'Base10'
class Derived10 extends Base10 {
    getName(): string {
        return "world";
    }
}

const d10 = new Derived10();
d10.printName();

// Abstract Construct Signatures
function greet3(ctor: typeof Base10) {
    // Cannot create an instance of an abstract class.
    const instance = new ctor();
    instance.printName();
}
function greet4(ctor: new() => Base10) {
    // Cannot create an instance of an abstract class.
    const instance = new ctor();
    instance.printName();
}
greet4(Derived10)
// rgument of type 'typeof Base10' is not assignable to parameter of type 'new () => Base10'.
//   Cannot assign an abstract constructor type to a non-abstract constructor type.
greet4(Base10)


class Point6 {
    x = 0;
    y = 0;
}

class Point7 {
    x = 0;
    y = 0;
}
// OK
const p:Point6 = new Point7();

class Person {
    name: string;
    age: number;
}

class Employee {
    name: string;
    age: number;
    salary: number;
}
// ok
const p4:Person = new Employee();
class Empty {}
function fn4(x:Empty) {

}

fn4(window);
fn4({});
fn4(fn4);